//==--- PropertiesBase.td - Baseline definitions for AST properties -------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

class HasProperties;

/// The type of the property.
class PropertyType<string typeName = ""> {
  /// The C++ type name for the type.
  string CXXName = !if(!ne(typeName, ""), typeName, NAME);

  /// Whether the C++ type should generally be passed around by reference.
  bit PassByReference = 0;

  /// Whether `const` should be prepended to the type when writing.
  bit ConstWhenWriting = 0;

  /// Given a value of type Optional<CXXName> bound as 'value', yield a
  /// CXXName that can be serialized into a DataStreamTypeWriter.
  string PackOptional = "";

  /// Given a value of type CXXName bound as 'value' that was deserialized
  /// by a DataStreamTypeReader, yield an Optional<CXXName>.
  string UnpackOptional = "";

  /// A list of types for which buffeers must be passed to the read
  /// operations.
  list<PropertyType> BufferElementTypes = [];
}

/// Property types that correspond to specific C++ enums.
class EnumPropertyType<string typeName = ""> : PropertyType<typeName> {}

/// Property types that correspond to a specific C++ class.
/// Supports optional values by using the null representation.
class RefPropertyType<string className> : PropertyType<className # "*"> {
  let PackOptional =
    "value ? *value : nullptr";
  let UnpackOptional =
    "value ? std::optional<" # CXXName # ">(value) : std::nullopt";
}

/// Property types that correspond to a specific subclass of another type.
class SubclassPropertyType<string className, PropertyType base>
    : RefPropertyType<className> {
  PropertyType Base = base;
  string SubclassName = className;
  let ConstWhenWriting = base.ConstWhenWriting;
}

/// Property types that support optional values by using their
/// default value.
class DefaultValuePropertyType<string typeName = ""> : PropertyType<typeName> {
  let PackOptional =
    "value ? *value : " # CXXName # "()";
  let UnpackOptional =
    "value.isNull() ? std::nullopt : std::optional<" # CXXName # ">(value)";
}

/// Property types that correspond to integer types and support optional
/// values by shifting the value over by 1.
class CountPropertyType<string typeName = ""> : PropertyType<typeName> {
  let PackOptional =
    "value ? *value + 1 : 0";
  let UnpackOptional =
    "value ? std::optional<" # CXXName # ">(value - 1) : std::nullopt";
}

def APInt : PropertyType<"llvm::APInt"> { let PassByReference = 1; }
def APSInt : PropertyType<"llvm::APSInt"> { let PassByReference = 1; }
def APValue : PropertyType { let PassByReference = 1; }
def APValueKind : EnumPropertyType<"APValue::ValueKind">;
def ArraySizeModifier : EnumPropertyType<"ArrayType::ArraySizeModifier">;
def AttrKind : EnumPropertyType<"attr::Kind">;
def AutoTypeKeyword : EnumPropertyType;
def Bool : PropertyType<"bool">;
def BuiltinTypeKind : EnumPropertyType<"BuiltinType::Kind">;
def BTFTypeTagAttr : PropertyType<"const BTFTypeTagAttr *">;
def CallingConv : EnumPropertyType;
def DeclarationName : PropertyType;
def DeclarationNameKind : EnumPropertyType<"DeclarationName::NameKind">;
def DeclRef : RefPropertyType<"Decl"> { let ConstWhenWriting = 1; }
  def CXXRecordDeclRef :
    SubclassPropertyType<"CXXRecordDecl", DeclRef>;
  def FunctionDeclRef :
    SubclassPropertyType<"FunctionDecl", DeclRef>;
  def NamedDeclRef :
    SubclassPropertyType<"NamedDecl", DeclRef>;
  def NamespaceDeclRef :
    SubclassPropertyType<"NamespaceDecl", DeclRef>;
  def NamespaceAliasDeclRef :
    SubclassPropertyType<"NamespaceAliasDecl", DeclRef>;
  def ObjCProtocolDeclRef :
    SubclassPropertyType<"ObjCProtocolDecl", DeclRef>;
  def ObjCTypeParamDeclRef :
    SubclassPropertyType<"ObjCTypeParamDecl", DeclRef>;
  def TagDeclRef :
    SubclassPropertyType<"TagDecl", DeclRef>;
  def TemplateDeclRef :
    SubclassPropertyType<"TemplateDecl", DeclRef>;
  def ConceptDeclRef :
    SubclassPropertyType<"ConceptDecl", DeclRef>;
  def TemplateTypeParmDeclRef :
    SubclassPropertyType<"TemplateTypeParmDecl", DeclRef>;
  def TemplateTemplateParmDeclRef :
    SubclassPropertyType<"TemplateTemplateParmDecl", DeclRef>;
  def UsingShadowDeclRef :
    SubclassPropertyType<"UsingShadowDecl", DeclRef>;
  def ValueDeclRef :
    SubclassPropertyType<"ValueDecl", DeclRef>;
def ElaboratedTypeKeyword : EnumPropertyType;
def ExtParameterInfo : PropertyType<"FunctionProtoType::ExtParameterInfo">;
def FixedPointSemantics : PropertyType<"llvm::FixedPointSemantics"> {
  let PassByReference = 1;
}
def Identifier : RefPropertyType<"IdentifierInfo"> { let ConstWhenWriting = 1; }
def LValuePathEntry : PropertyType<"APValue::LValuePathEntry">;
def LValuePathSerializationHelper :
    PropertyType<"APValue::LValuePathSerializationHelper"> {
  let BufferElementTypes = [ LValuePathEntry ];
}
def NestedNameSpecifier : PropertyType<"NestedNameSpecifier *">;
def NestedNameSpecifierKind :
  EnumPropertyType<"NestedNameSpecifier::SpecifierKind">;
def OverloadedOperatorKind : EnumPropertyType;
def Qualifiers : PropertyType;
def QualType : DefaultValuePropertyType;
def RefQualifierKind : EnumPropertyType;
def Selector : PropertyType;
def SourceLocation : PropertyType;
def StmtRef : RefPropertyType<"Stmt"> { let ConstWhenWriting = 1; }
  def ExprRef : SubclassPropertyType<"Expr", StmtRef>;
def TemplateArgument : PropertyType;
def TemplateArgumentKind : EnumPropertyType<"TemplateArgument::ArgKind">;
def TemplateName : DefaultValuePropertyType;
def TemplateNameKind : EnumPropertyType<"TemplateName::NameKind">;
def TypeOfKind : EnumPropertyType<"TypeOfKind">;
def UInt32 : CountPropertyType<"uint32_t">;
def UInt64 : CountPropertyType<"uint64_t">;
def UnaryTypeTransformKind : EnumPropertyType<"UnaryTransformType::UTTKind">;
def VectorKind : EnumPropertyType<"VectorType::VectorKind">;

def ExceptionSpecInfo : PropertyType<"FunctionProtoType::ExceptionSpecInfo"> {
  let BufferElementTypes = [ QualType ];
}

/// Arrays.  The corresponding C++ type is ArrayRef of the corresponding
/// C++ type of the element.
class Array<PropertyType element> : PropertyType {
  PropertyType Element = element;
  let BufferElementTypes = [ element ];
}

/// std::optional<T>.  The corresponding C++ type is generally just the
/// corresponding C++ type of the element.
///
/// Optional<Unsigned> may restrict the range of the operand for some
/// serialization clients.
class Optional<PropertyType element> : PropertyType {
  PropertyType Element = element;
  let PassByReference = element.PassByReference;
}

/// A property of an AST node.
class Property<string name, PropertyType type> {
  HasProperties Class;
  string Name = name;
  PropertyType Type = type;

  /// A function for reading the property, expressed in terms of a variable
  /// "node".
  code Read;

  /// Code specifying when this property is available.  Can be defined
  /// in terms of other properties, in which case this property must be
  /// read/written after those properties.  Using this will make the
  /// value Optional when deserializing.
  ///
  /// FIXME: the emitter doesn't yet force dependent properties to be
  /// read/written later; this only works if the properties used in the
  /// condition happen to be written first.
  code Conditional = "";
}

/// A rule for declaring helper variables when read properties from a
/// value of this type.  Note that this means that this code is actually
/// run when *writing* values of this type; however, naming this
/// `ReadHelper` makes the connection to the `Read` operations on the
/// properties much clearer.
class ReadHelper<code _code> {
  HasProperties Class;

  /// Code which will be run when writing objects of this type before
  /// writing any of the properties, specified in terms of a variable
  /// `node`.
  code Code = _code;
}

/// A rule for creating objects of this type.
class Creator<code create> {
  HasProperties Class;

  /// A function for creating values of this kind, expressed in terms of a
  /// variable `ctx` of type `ASTContext &`.  Must also refer to all of the
  /// properties by name.
  code Create = create;
}

/// A rule which overrides some of the normal rules.
class Override {
  HasProperties Class;

  /// Properties from base classes that should be ignored.
  list<string> IgnoredProperties = [];
}

/// A description of how to break a type into cases.  Providing this and
/// an exhaustive list of the cases will cause AbstractBasic{Reader,Writer}
/// to be generated with a default implementation of how to read the
/// type.
///
/// Creator rules for the cases can additionally access a variable
/// `kind` of the KindType.
class PropertyTypeKind<PropertyType type,
                       PropertyType kindType,
                       string readCode> {
  /// The type for which this describes cases.
  PropertyType Type = type;

  /// The type of this type's kind enum.
  PropertyType KindType = kindType;

  /// The property name to use for the kind.
  string KindPropertyName = "kind";

  /// An expression which reads the kind from a value, expressed in terms
  /// of a variable `node`.
  string Read = readCode;
}

/// One of the options for representing a particular type.
class PropertyTypeCase<PropertyType type, string name> : HasProperties {
  /// The type of which this is a case.
  PropertyType Type = type;

  /// The name of the case (a value of the type's kind enum).
  string Name = name;
}

// Type cases for APValue.
def : PropertyTypeKind<APValue, APValueKind,
                       "node.getKind()">;
let Class = PropertyTypeCase<APValue, "None"> in {
  def : Creator<[{ return APValue(); }]>;
}
let Class = PropertyTypeCase<APValue, "Indeterminate"> in {
  def : Creator<[{ return APValue::IndeterminateValue(); }]>;
}
let Class = PropertyTypeCase<APValue, "Int"> in {
  def : Property<"value", APSInt> {
    let Read = [{ node.getInt() }];
  }
  def : Creator<[{ return APValue(value); }]>;
}
let Class = PropertyTypeCase<APValue, "Float"> in {
  def : Property<"semantics", UInt32> {
    let Read = [{
      static_cast<uint32_t>(
        llvm::APFloatBase::SemanticsToEnum(node.getFloat().getSemantics()))
    }];
  }
  def : Property<"value", APInt> {
    let Read = [{ node.getFloat().bitcastToAPInt() }];
  }
  def : Creator<[{
    const llvm::fltSemantics &floatSema = llvm::APFloatBase::EnumToSemantics(
        static_cast<llvm::APFloatBase::Semantics>(semantics));
    return APValue(llvm::APFloat(floatSema, value));
  }]>;
}
let Class = PropertyTypeCase<APValue, "FixedPoint"> in {
  def : Property<"semantics", FixedPointSemantics> {
    let Read = [{ node.getFixedPoint().getSemantics() }];
  }
  def : Property<"value", APSInt> {
    let Read = [{ node.getFixedPoint().getValue() }];
  }
  def : Creator<[{
    return APValue(llvm::APFixedPoint(std::move(value), semantics));
  }]>;
}
let Class = PropertyTypeCase<APValue, "ComplexInt"> in {
  def : Property<"real", APSInt> {
    let Read = [{ node.getComplexIntReal() }];
  }
  def : Property<"imag", APSInt> {
    let Read = [{ node.getComplexIntImag() }];
  }
  def : Creator<[{ return APValue(real, imag); }]>;
}
let Class = PropertyTypeCase<APValue, "ComplexFloat"> in {
  def : ReadHelper<[{
    auto sema = llvm::APFloatBase::SemanticsToEnum(
        node.getComplexFloatReal().getSemantics());
    assert(sema == llvm::APFloatBase::SemanticsToEnum(
        node.getComplexFloatImag().getSemantics()));
  }]>;
  def : Property<"semantics", UInt32> {
    let Read = [{ static_cast<uint32_t>(sema) }];
  }
  def : Property<"real", APInt> {
    let Read = [{ node.getComplexFloatReal().bitcastToAPInt() }];
  }
  def : Property<"imag", APInt> {
    let Read = [{ node.getComplexFloatImag().bitcastToAPInt() }];
  }
  def : Creator<[{
    const llvm::fltSemantics &sema = llvm::APFloatBase::EnumToSemantics(
        static_cast<llvm::APFloatBase::Semantics>(semantics));
    return APValue(llvm::APFloat(sema, real),
                   llvm::APFloat(sema, imag));
  }]>;
}
let Class = PropertyTypeCase<APValue, "Vector"> in {
  def : ReadHelper<[{
    SmallVector<APValue, 4> buffer;
    unsigned len = node.getVectorLength();
    for (unsigned i = 0; i < len; ++i)
      buffer.push_back(node.getVectorElt(i));
  }]>;
  def : Property<"elements", Array<APValue>> {
    let Read = [{ buffer }];
  }
  def : Creator<[{
    APValue result;
    result.MakeVector();
    unsigned length = elements.size();
    (void)result.setVectorUninit(length);
    for (unsigned i = 0; i < length; i++)
      result.getVectorElt(i) = elements[i];
    return result;
  }]>;
}
let Class = PropertyTypeCase<APValue, "Array"> in {
  def : ReadHelper<[{
    SmallVector<APValue, 4> buffer{};
    unsigned initLength = node.getArrayInitializedElts();
    for (unsigned i = 0; i < initLength; ++i)
      buffer.push_back(node.getArrayInitializedElt(i));
    if (node.hasArrayFiller())
      buffer.push_back(node.getArrayFiller());
  }]>;
  def : Property<"totalLength", UInt32> {
    let Read = [{ node.getArraySize() }];
  }
  def : Property<"hasFiller", Bool> {
    let Read = [{ node.hasArrayFiller() }];
  }
  def : Property<"elements", Array<APValue>> {
    let Read = [{ buffer }];
  }
  def : Creator<[{
    APValue result;
    unsigned initLength = elements.size() - (hasFiller ? 1 : 0);
    result.MakeArray(initLength, totalLength);
    for (unsigned i = 0; i < initLength; ++i)
      result.getArrayInitializedElt(i) = elements[i];
    if (hasFiller)
      result.getArrayFiller() = elements.back();
    return result;
  }]>;
}
let Class = PropertyTypeCase<APValue, "Struct"> in {
  def : ReadHelper<[{
    SmallVector<APValue, 4> structBases;
    unsigned numBases = node.getStructNumBases();
    for (unsigned i = 0; i < numBases; ++i)
      structBases.push_back(node.getStructBase(i));
    SmallVector<APValue, 4> structFields;
    unsigned numFields = node.getStructNumFields();
    for (unsigned i = 0; i < numFields; ++i)
      structFields.push_back(node.getStructField(i));
  }]>;
  def : Property<"bases", Array<APValue>> {
    let Read = [{ structBases }];
  }
  def : Property<"fields", Array<APValue>> {
    let Read = [{ structFields }];
  }
  def : Creator<[{
    APValue result;
    result.MakeStruct(bases.size(), fields.size());
    for (unsigned i = 0; i < bases.size(); ++i)
      result.getStructBase(i) = bases[i];
    for (unsigned i = 0; i < fields.size(); ++i)
      result.getStructField(i) = fields[i];
    return result;
  }]>;
}
let Class = PropertyTypeCase<APValue, "Union"> in {
  def : Property<"fieldDecl", DeclRef> {
    let Read = [{ node.getUnionField() }];
  }
  def : Property<"value", APValue> {
    let Read = [{ node.getUnionValue() }];
  }
  def : Creator<[{
    return APValue(cast<clang::FieldDecl>(fieldDecl), std::move(value));
  }]>;
}
let Class = PropertyTypeCase<APValue, "AddrLabelDiff"> in {
  def : Property<"lhs", StmtRef> {
    let Read = [{ const_cast<AddrLabelExpr *>(node.getAddrLabelDiffLHS()) }];
  }
  def : Property<"rhs", StmtRef> {
    let Read = [{ const_cast<AddrLabelExpr *>(node.getAddrLabelDiffRHS()) }];
  }
  def : Creator<[{
    return APValue(cast<AddrLabelExpr>(lhs), cast<AddrLabelExpr>(rhs));
  }]>;
}
let Class = PropertyTypeCase<APValue, "MemberPointer"> in {
  def : Property<"isDerived", Bool> {
    let Read = [{ node.isMemberPointerToDerivedMember() }];
  }
  def : Property<"member", ValueDeclRef> {
    let Read = [{ node.getMemberPointerDecl() }];
  }
  def : Property<"memberPath", Array<CXXRecordDeclRef>> {
    let Read = [{ node.getMemberPointerPath() }];
  }
  def : Creator<[{
    APValue result;
    unsigned pathSize = memberPath.size();
    const CXXRecordDecl **pathArray =
        result.setMemberPointerUninit(member, isDerived, pathSize).data();
    for (unsigned i = 0; i < pathSize; ++i)
      pathArray[i] = memberPath[i]->getCanonicalDecl();
    return result;
  }]>;
}
let Class = PropertyTypeCase<APValue, "LValue"> in {
  def : ReadHelper<[{
    auto lvalueBase = node.getLValueBase();
    const Expr *expr =
        lvalueBase ? lvalueBase.dyn_cast<const Expr *>() : nullptr;
    bool lvalueBaseIsExpr = (bool) expr;
    bool lvalueBaseIsTypeInfo = lvalueBase.is<TypeInfoLValue>();
    QualType elemTy;
    if (lvalueBase) {
      if (lvalueBaseIsTypeInfo) {
        elemTy = lvalueBase.getTypeInfoType();
      } else if (lvalueBaseIsExpr) {
        elemTy = expr->getType();
      } else {
        elemTy = lvalueBase.get<const ValueDecl *>()->getType();
      }
    }
  }]>;
  def : Property<"hasLValuePath", Bool> {
    let Read = [{ node.hasLValuePath() }];
  }
  def : Property<"isLValueOnePastTheEnd", Bool> {
    let Read = [{ node.isLValueOnePastTheEnd() }];
  }
  def : Property<"isExpr", Bool> {
    let Read = [{ lvalueBaseIsExpr }];
  }
  def : Property<"isTypeInfo", Bool> {
    let Read = [{ lvalueBaseIsTypeInfo }];
  }
  def : Property<"hasBase", Bool> {
    let Read = [{ static_cast<bool>(lvalueBase) }];
  }
  def : Property<"isNullPtr", Bool> {
    let Read = [{ node.isNullPointer() }];
  }
  def : Property<"typeInfo", QualType> {
    let Conditional = [{ hasBase && isTypeInfo }];
    let Read = [{
      QualType(node.getLValueBase().get<TypeInfoLValue>().getType(), 0)
    }];
  }
  def : Property<"type", QualType> {
    let Conditional = [{ hasBase && isTypeInfo }];
    let Read = [{ node.getLValueBase().getTypeInfoType() }];
  }
  def : Property<"callIndex", UInt32> {
    let Conditional = [{ hasBase && !isTypeInfo }];
    let Read = [{ node.getLValueBase().getCallIndex() }];
  }
  def : Property<"version", UInt32> {
    let Conditional = [{ hasBase && !isTypeInfo }];
    let Read = [{ node.getLValueBase().getVersion() }];
  }
  def : Property<"stmt", StmtRef> {
    let Conditional = [{ hasBase && !isTypeInfo && isExpr }];
    let Read = [{ const_cast<Expr *>(expr) }];
  }
  def : Property<"decl", DeclRef> {
    let Conditional = [{ hasBase && !isTypeInfo && !isExpr }];
    let Read = [{ lvalueBase.get<const ValueDecl *>() }];
  }
  def : Property<"offsetQuantity", UInt32> {
    let Read = [{ node.getLValueOffset().getQuantity() }];
  }
  def : Property<"lvaluePath", LValuePathSerializationHelper> {
    let Conditional = [{ hasLValuePath }];
    let Read = [{
      APValue::LValuePathSerializationHelper(node.getLValuePath(), elemTy)
    }];
  }
  def : Creator<[{
    (void)ctx;
    APValue::LValueBase base;
    if (hasBase) {
      if (isTypeInfo) {
        base = APValue::LValueBase::getTypeInfo(
            TypeInfoLValue(typeInfo->getTypePtr()), *type);
      } else if (isExpr) {
        base = APValue::LValueBase(cast<Expr>(*stmt),
                                   *callIndex, *version);
      } else {
        base = APValue::LValueBase(cast<ValueDecl>(*decl),
                                   *callIndex, *version);
      }
    }
    CharUnits offset = CharUnits::fromQuantity(offsetQuantity);
    APValue result;
    result.MakeLValue();
    if (!hasLValuePath) {
      result.setLValue(base, offset, APValue::NoLValuePath{}, isNullPtr);
      return result;
    }
    auto pathLength = lvaluePath->Path.size();
    APValue::LValuePathEntry *path = result.setLValueUninit(
        base, offset, pathLength, isLValueOnePastTheEnd, isNullPtr).data();
    llvm::copy(lvaluePath->Path, path);
    return result;
  }]>;
}

// Type cases for DeclarationName.
def : PropertyTypeKind<DeclarationName, DeclarationNameKind,
                       "node.getNameKind()">;
let Class = PropertyTypeCase<DeclarationName, "Identifier"> in {
  def : Property<"identifier", Identifier> {
    let Read = [{ node.getAsIdentifierInfo() }];
  }
  def : Creator<[{
    return DeclarationName(identifier);
  }]>;
}
foreach count = ["Zero", "One", "Multi"] in {
  let Class = PropertyTypeCase<DeclarationName, "ObjC"#count#"ArgSelector"> in {
    def : Property<"selector", Selector> {
      let Read = [{ node.getObjCSelector() }];
    }
    def : Creator<[{
      return DeclarationName(selector);
    }]>;
  }
}
foreach kind = ["Constructor", "Destructor", "ConversionFunction"] in {
  let Class = PropertyTypeCase<DeclarationName, "CXX"#kind#"Name"> in {
    def : Property<"type", QualType> {
      let Read = [{ node.getCXXNameType() }];
    }
    def : Creator<[{
      return ctx.DeclarationNames.getCXX}]#kind#[{Name(
               ctx.getCanonicalType(type));
    }]>;
  }
}
let Class = PropertyTypeCase<DeclarationName, "CXXDeductionGuideName"> in {
  def : Property<"declaration", TemplateDeclRef> {
    let Read = [{ node.getCXXDeductionGuideTemplate() }];
  }
  def : Creator<[{
    return ctx.DeclarationNames.getCXXDeductionGuideName(declaration);
  }]>;
}
let Class = PropertyTypeCase<DeclarationName, "CXXOperatorName"> in {
  def : Property<"operatorKind", OverloadedOperatorKind> {
    let Read = [{ node.getCXXOverloadedOperator() }];
  }
  def : Creator<[{
    return ctx.DeclarationNames.getCXXOperatorName(operatorKind);
  }]>;
}
let Class = PropertyTypeCase<DeclarationName, "CXXLiteralOperatorName"> in {
  def : Property<"identifier", Identifier> {
    let Read = [{ node.getCXXLiteralIdentifier() }];
  }
  def : Creator<[{
    return ctx.DeclarationNames.getCXXLiteralOperatorName(identifier);
  }]>;
}
let Class = PropertyTypeCase<DeclarationName, "CXXUsingDirective"> in {
  def : Creator<[{
    return DeclarationName::getUsingDirectiveName();
  }]>;
}

// Type cases for TemplateName.
def : PropertyTypeKind<TemplateName, TemplateNameKind, "node.getKind()">;
let Class = PropertyTypeCase<TemplateName, "Template"> in {
  def : Property<"declaration", TemplateDeclRef> {
    let Read = [{ node.getAsTemplateDecl() }];
  }
  def : Creator<[{
    return TemplateName(declaration);
  }]>;
}

let Class = PropertyTypeCase<TemplateName, "UsingTemplate"> in {
  def : Property<"foundDecl", UsingShadowDeclRef> {
    let Read = [{ node.getAsUsingShadowDecl() }];
  }
  def : Creator<[{
    return TemplateName(foundDecl);
  }]>;
}

let Class = PropertyTypeCase<TemplateName, "OverloadedTemplate"> in {
  def : Property<"overloads", Array<NamedDeclRef>> {
    let Read = [{ node.getAsOverloadedTemplate()->decls() }];
  }
  def : Creator<[{
    // Copy into an UnresolvedSet to satisfy the interface.
    UnresolvedSet<8> overloadSet;
    for (auto overload : overloads) {
      overloadSet.addDecl(overload);
    }

    return ctx.getOverloadedTemplateName(overloadSet.begin(),
                                         overloadSet.end());
  }]>;
}
let Class = PropertyTypeCase<TemplateName, "AssumedTemplate"> in {
  def : Property<"name", DeclarationName> {
    let Read = [{ node.getAsAssumedTemplateName()->getDeclName() }];
  }
  def : Creator<[{
    return ctx.getAssumedTemplateName(name);
  }]>;
}
let Class = PropertyTypeCase<TemplateName, "QualifiedTemplate"> in {
  def : ReadHelper<[{
    auto qtn = node.getAsQualifiedTemplateName();
  }]>;
  def : Property<"qualifier", NestedNameSpecifier> {
    let Read = [{ qtn->getQualifier() }];
  }
  def : Property<"hasTemplateKeyword", Bool> {
    let Read = [{ qtn->hasTemplateKeyword() }];
  }
  def : Property<"underlyingTemplateName", TemplateName> {
    let Read = [{ qtn->getUnderlyingTemplate() }];
  }
  def : Creator<[{
    return ctx.getQualifiedTemplateName(qualifier, hasTemplateKeyword,
                                        underlyingTemplateName);
  }]>;
}
let Class = PropertyTypeCase<TemplateName, "DependentTemplate"> in {
  def : ReadHelper<[{
    auto dtn = node.getAsDependentTemplateName();
  }]>;
  def : Property<"qualifier", NestedNameSpecifier> {
    let Read = [{ dtn->getQualifier() }];
  }
  def : Property<"identifier", Optional<Identifier>> {
    let Read = [{ makeOptionalFromPointer(
                    dtn->isIdentifier()
                      ? dtn->getIdentifier()
                      : nullptr) }];
  }
  def : Property<"operatorKind", OverloadedOperatorKind> {
    let Conditional = [{ !identifier }];
    let Read = [{ dtn->getOperator() }];
  }
  def : Creator<[{
    if (identifier) {
      return ctx.getDependentTemplateName(qualifier, *identifier);
    } else {
      return ctx.getDependentTemplateName(qualifier, *operatorKind);
    }
  }]>;
}
let Class = PropertyTypeCase<TemplateName, "SubstTemplateTemplateParm"> in {
  def : ReadHelper<[{
    auto parm = node.getAsSubstTemplateTemplateParm();
  }]>;
  def : Property<"replacement", TemplateName> {
    let Read = [{ parm->getReplacement() }];
  }
  def : Property<"associatedDecl", DeclRef> {
    let Read = [{ parm->getAssociatedDecl() }];
  }
  def : Property<"index", UInt32> {
    let Read = [{ parm->getIndex() }];
  }
  def : Property<"packIndex", Optional<UInt32>> {
    let Read = [{ parm->getPackIndex() }];
  }
  def : Creator<[{
    return ctx.getSubstTemplateTemplateParm(replacement, associatedDecl, index, packIndex);
  }]>;
}
let Class = PropertyTypeCase<TemplateName, "SubstTemplateTemplateParmPack"> in {
  def : ReadHelper<[{
    auto parm = node.getAsSubstTemplateTemplateParmPack();
  }]>;
  def : Property<"argumentPack", TemplateArgument> {
    let Read = [{ parm->getArgumentPack() }];
  }
  def : Property<"associatedDecl", DeclRef> {
    let Read = [{ parm->getAssociatedDecl() }];
  }
  def : Property<"index", UInt32> {
    let Read = [{ parm->getIndex() }];
  }
  def : Property<"final", Bool> {
    let Read = [{ parm->getFinal() }];
  }
  def : Creator<[{
    return ctx.getSubstTemplateTemplateParmPack(argumentPack, associatedDecl, index, final);
  }]>;
}

// Type cases for TemplateArgument.
def : PropertyTypeKind<TemplateArgument, TemplateArgumentKind,
                       "node.getKind()">;
let Class = PropertyTypeCase<TemplateArgument, "Null"> in {
  def : Creator<[{
    return TemplateArgument();
  }]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Type"> in {
  def : Property<"type", QualType> {
    let Read = [{ node.getAsType() }];
  }
  def : Property<"isDefaulted", Bool> {
    let Read = [{ node.getIsDefaulted() }];
  }
  def : Creator<[{
    return TemplateArgument(type, /* isNullPtr */ false, isDefaulted);
  }]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Declaration"> in {
  def : Property<"declaration", ValueDeclRef> {
    let Read = [{ node.getAsDecl() }];
  }
  def : Property<"parameterType", QualType> {
    let Read = [{ node.getParamTypeForDecl() }];
  }
  def : Property<"isDefaulted", Bool> {
    let Read = [{ node.getIsDefaulted() }];
  }
  def : Creator<[{
    return TemplateArgument(declaration, parameterType, isDefaulted);
  }]>;
}
let Class = PropertyTypeCase<TemplateArgument, "NullPtr"> in {
  def : Property<"type", QualType> {
    let Read = [{ node.getNullPtrType() }];
  }
  def : Property<"isDefaulted", Bool> {
    let Read = [{ node.getIsDefaulted() }];
  }
  def : Creator<[{
    return TemplateArgument(type, /*nullptr*/ true, isDefaulted);
  }]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Integral"> in {
  def : Property<"value", APSInt> {
    let Read = [{ node.getAsIntegral() }];
  }
  def : Property<"type", QualType> {
    let Read = [{ node.getIntegralType() }];
  }
  def : Property<"isDefaulted", Bool> {
    let Read = [{ node.getIsDefaulted() }];
  }
  def : Creator<[{
    return TemplateArgument(ctx, value, type, isDefaulted);
  }]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Template"> in {
  def : Property<"name", TemplateName> {
    let Read = [{ node.getAsTemplateOrTemplatePattern() }];
  }
  def : Property<"isDefaulted", Bool> {
    let Read = [{ node.getIsDefaulted() }];
  }
  def : Creator<[{
    return TemplateArgument(name, isDefaulted);
  }]>;
}
let Class = PropertyTypeCase<TemplateArgument, "TemplateExpansion"> in {
  def : Property<"name", TemplateName> {
    let Read = [{ node.getAsTemplateOrTemplatePattern() }];
  }
  def : Property<"numExpansions", Optional<UInt32>> {
    let Read = [{
      // Translate unsigned -> uint32_t just in case.
      llvm::transformOptional(node.getNumTemplateExpansions(),
                              [](unsigned i) { return uint32_t(i); })
    }];
  }
  def : Property<"isDefaulted", Bool> {
    let Read = [{ node.getIsDefaulted() }];
  }
  def : Creator<[{
    auto numExpansionsUnsigned = llvm::transformOptional(
        numExpansions, [](uint32_t i) { return unsigned(i); });

    return TemplateArgument(name, numExpansionsUnsigned, isDefaulted);
  }]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Expression"> in {
  def : Property<"expression", ExprRef> {
    let Read = [{ node.getAsExpr() }];
  }
  def : Property<"isDefaulted", Bool> {
    let Read = [{ node.getIsDefaulted() }];
  }
  def : Creator<[{
    return TemplateArgument(expression, isDefaulted);
  }]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Pack"> in {
  def : Property<"elements", Array<TemplateArgument>> {
    let Read = [{ node.pack_elements() }];
  }
  def : Creator<[{
    // Copy the pack into the ASTContext.
    TemplateArgument *ctxElements = new (ctx) TemplateArgument[elements.size()];
    for (size_t i = 0, e = elements.size(); i != e; ++i)
      ctxElements[i] = elements[i];
    return TemplateArgument(llvm::ArrayRef(ctxElements, elements.size()));
  }]>;
}
